The purpose of this notebook is to create a neighborhood index that combines crime and cleanliness data. The index relies on 311 service request data and Part 1 violent crime data, both availble on Open Baltimore.
Brief Description of The Index
The index primarily relies on counts of violent crime and service requests per 1,000 residents. This is done to remove the influence of population on the counts of these events. The per-capita metrics are then scaled to between 0 and 1 to build up the overall index.
All Part 1 crime types are used.
The SR types used are:
Illegal Dumping:
- HCD-Illegal Dumping
- SW-SIU Clean Up
Property Maintenance:
- HCD-Sanitation Property
- SW-Boarding
- SW-HGW
- SW-Cleaning
Right-of-Way Cleaning
- SW-Dirty Street
- SW-Dirty Alley
knitr::opts_chunk$set(echo = T,
warning = F,
message = F,
include = T,
fig.width = 4,
fig.height = 3)
library(tidyverse)
library(ggiteam)
library(lubridate)
library(readxl)
library(spdplyr)
library(leaflet)
library(RSocrata)
library(geojsonsf)
library(ggmap)
library(rgdal)
library(scales)
library(sf)
library(htmltools)
register_google("AIzaSyByyKUj4QaAlzNtPJPw-HkEEr7d4hTawO0")
sr_query <- paste0("https://data.baltimorecity.gov/resource/9agw-sxsr.json",
"?$where=",
"((srtype like 'SW-Boarding' OR ",
"srtype like 'SW-Cleaning' OR ",
"srtype like 'SW-HGW' OR ",
"srtype like 'SW-Dirty Alley' OR ",
"srtype like 'SW-Dirty Street' OR ",
"srtype like 'HCD-Illegal Dumping' OR ",
"srtype like 'HCD-SIU' OR ",
"srtype like 'SW-SIU Clean Up' OR ",
"srtype like 'HCD-Sanitation Property') AND ",
"date_extract_y(createddate)==2019)")
sr <- read.socrata(url = sr_query)
sr <- sr %>%
mutate(
latitude = as.numeric(latitude),
longitude = as.numeric(longitude),
neighborhood = toupper(neighborhood))
crime_query <- paste0("https://data.baltimorecity.gov/resource/wsfq-mvij.json?$where=",
"(Description like 'HOMICIDE' OR ",
"Description like 'SHOOTING' OR ",
"Description like 'AGG. ASSAULT' OR ",
"Description like 'RAPE' OR ",
"contains(Description, 'ROBBERY')) AND ",
"date_extract_y(crimedate)==2019")
crime <- read.socrata(crime_query)
cityline_url <- "https://data.baltimorecity.gov/resource/nfyc-pejx.geojson"
# surely there is a better way, and i tried, but couldn't get anything else to work.
# from geojson to sf to spatial df. geojson_sp didn't work for me.
cityline <- geojson_sf(cityline_url)
cityline <- as_Spatial(cityline)
cityline <- spTransform(cityline, CRS("+init=epsg:4326"))
hoods.url <- "https://data.baltimorecity.gov/resource/h3fx-54q3.geojson"
# surely there is a better way, and i tried, but couldn't get anything else to work.
# from geojson to sf to spatial df. geojson_sp didn't work for me.
hoods <- geojson_sf(hoods.url)
hoods <- as_Spatial(hoods)
hoods <- spTransform(hoods, CRS("+init=epsg:4326"))
# join acs data to hoods
hoods_pop <- read_excel("../data/raw/Neighborhood Pop.xlsx")
hoods@data <- left_join(hoods@data, hoods_pop, by = c("label" = "Name"))
# bpd facilities to remove
facilities <- c(
"300 E MADISON ST", # central booking
"1400 E NORTH AVE", # district court
"1900 ARGONNE DR", # northeastern
"2200 W Cold Spring Ln", # northern
"1000 N MOUNT ST", # western
"5200 REISTERSTOWN RD ", # northwest
"1600 EDISON HWY", # eastern
"400 FONTHILL AVE", # southwest
"0 CHERRY HILL RD", # southern
"600 E FAYETTE ST", # central/hq
"5700 EASTERN AVE", # southeast
"2000 W BALTIMORE ST", # bon secours
"4000 DEEPWOOD RD", # loch raven va
"300 N GAY ST", # juv booking
"4900 EASTERN AV", # bayview
"1800 ORLEANS ST", # jhh downtown
"600 N WOLFE ST", # jhh downtown
"0 S GREENE ST", # umd medical center
"3400 N CALVERT ST" # union memorial
)
Population distribution of Baltimore neighborhoods.
hoods@data %>%
ggplot(aes(ACS_2013_2017)) +
geom_histogram() +
theme_iteam_google_docs()

Tenth percentile for population:
quantile(hoods$ACS_2013_2017, 0.1, na.rm = T)
10%
174.7548
Analysis
crime_labeled <- crime %>%
filter(
!is.na(latitude),
!(location %in% facilities)
) %>%
st_as_sf(coords = c("longitude", "latitude"), crs = 4326) %>%
st_join(st_as_sf(hoods), join = st_within, left = F)
crime_hood_counts <- crime_labeled %>%
data.frame() %>%
count(description, label) %>%
spread(key = description, value = n) %>%
replace(is.na(.), 0) %>%
transmute(
label = label,
agg_assault = `AGG. ASSAULT`,
rape = RAPE,
homicide_shootings = HOMICIDE + SHOOTING,
robbery = `ROBBERY - COMMERCIAL` +
`ROBBERY - CARJACKING` +
`ROBBERY - RESIDENCE` +
`ROBBERY - STREET`)
sr_labeled <- sr %>%
filter(
!is.na(latitude)
) %>%
st_as_sf(coords = c("longitude", "latitude"), crs = 4326) %>%
st_join(st_as_sf(hoods), join = st_within, left = F)
sr_hood_counts <- sr_labeled %>%
data.frame() %>%
count(srtype, label) %>%
spread(key = srtype, value = n) %>%
replace(is.na(.), 0) %>%
transmute(
label = label,
illegal_dumping = `HCD-Illegal Dumping` + `SW-SIU Clean Up`,
property_maintenance = `HCD-Sanitation Property` +
`SW-Boarding` +
`SW-Cleaning` +
`SW-HGW`,
right_of_way = `SW-Dirty Street` + `SW-Dirty Alley`
)
# to per 1,000 residents
to_per_capita <- function(x){1000 * x / hoods$ACS_2013_2017}
hood_metrics <- hoods
hood_metrics@data <- hood_metrics@data %>%
select(-acres,
-nbrdesc,
-shape_leng,
-shape_area,
-color_2,
-Decennial2010)
hood_metrics@data <- hood_metrics@data %>%
left_join(
crime_hood_counts,
by = "label") %>%
left_join(
sr_hood_counts,
by = "label"
) %>%
mutate_at(
vars(
illegal_dumping,
property_maintenance,
right_of_way,
homicide_shootings,
robbery,
agg_assault,
rape),
list(per_1000_res = to_per_capita))
Distribution of per capita rates of crime and service requests:
hood_metrics@data %>%
select(
label,
illegal_dumping_per_1000_res,
property_maintenance_per_1000_res,
right_of_way_per_1000_res,
homicide_shootings_per_1000_res,
robbery_per_1000_res,
agg_assault_per_1000_res,
rape_per_1000_res
) %>%
pivot_longer(-label) %>%
ggplot(aes(value)) +
facet_wrap(~name, scales = "free") +
geom_histogram(bins = 50) +
theme_iteam_google_docs() +
labs(title = "2019 Events per 1,000 Residents")

In all cases, outlier neighborhoods with very small populations skew data.
Try removing neighborhoods with less than 150 residents (10% of neighborhoods). These are typically parks, industrial areas, etc.
hood_metrics@data %>%
filter(ACS_2013_2017 >= 150) %>%
select(
label,
illegal_dumping_per_1000_res,
property_maintenance_per_1000_res,
right_of_way_per_1000_res,
homicide_shootings_per_1000_res,
robbery_per_1000_res,
agg_assault_per_1000_res,
rape_per_1000_res
) %>%
pivot_longer(-label) %>%
ggplot(aes(value)) +
facet_wrap(~name, scales = "free") +
geom_histogram(bins = 50) +
theme_iteam_google_docs() +
labs(title = "2019 Events per 1,000 Residents\nNeighborhoods with >= 150 Residents")

Going forward, removing neighborhoods with less than 150 residents.
hood_metrics_over150 <- subset(hood_metrics, hood_metrics$ACS_2013_2017 > 150)
hood_metrics_over150 <- subset(hood_metrics_over150, !grepl("INDUSTRIAL", label, ignore.case = T))
Number of neighborhoods with at least one homicide or shooting:
hood_metrics_over150@data %>%
count(homicide_shootings >= 1)
Will likely assign 1 to neighborhoods with at least 1 homicide or shooting, 0 to all others. This will ensure neighborhoods with gun violence are given precedence.
Building The Index
All parameters will be scaled to between 0 and 1 because variables are on different scales.
# scale per capita
scale_index <- function(x){rescale(x, to = c(0,1))}
hood_metrics_over150@data <- hood_metrics_over150@data %>%
mutate(
homicide_shootings_index = ifelse(homicide_shootings > 0, 1, 0),
rape_index = ifelse(rape > 0, 1, 0)) %>%
mutate_at(
vars(
illegal_dumping_per_1000_res,
property_maintenance_per_1000_res,
right_of_way_per_1000_res,
homicide_shootings_per_1000_res,
robbery_per_1000_res,
agg_assault_per_1000_res,
rape_per_1000_res),
list(index = scale_index)
)
# plot rescaled data
hood_metrics_over150@data %>%
select(
label,
illegal_dumping_per_1000_res_index,
property_maintenance_per_1000_res_index,
right_of_way_per_1000_res_index,
homicide_shootings_per_1000_res_index,
robbery_per_1000_res_index,
agg_assault_per_1000_res_index,
rape_per_1000_res_index
) %>%
pivot_longer(-label) %>%
ggplot(aes(value)) +
facet_wrap(~name, scales = "free") +
geom_histogram(bins = 50) +
theme_iteam_google_docs() +
labs(title = "Standardized (between 0 and 1) 2019 Events per 1,000 Residents\nNeighborhoods with >= 150 Residents")

Again, all metrics have been scaled to between 0 and 1. Combine crime metrics into a homicide and shooting parameter (0 if none in neighborhood, 1 if at least 1) and an “other violent crime” parameter where:
\[ HomicideShootingsIndex = \begin{cases}
1 & \text{if $N_{homicides}+N_{shootings} > 0$ } \\
0 & \text{otherwise} \end{cases}\]
\[ OtherViolentCrimeIndex = \frac{1}{3}(RapeIndex+ AggAssaultIndex + RobberyIndex) \]
Combining the two crime indices:
\[ CrimeIndex = \frac{1}{2} (HomicideShootingsIndex + OtherViolentCrimeIndex) \]
# create combined index
hood_metrics_over150@data <- hood_metrics_over150@data %>%
rowwise() %>%
mutate(
other_violent_crime_index =
mean(
c(rape_per_1000_res_index,
agg_assault_per_1000_res_index,
robbery_per_1000_res_index),
na.rm = T),
grime_index = mean(
c(illegal_dumping_per_1000_res_index,
property_maintenance_per_1000_res_index,
right_of_way_per_1000_res_index),
na.rm = T)
) %>%
ungroup() %>%
mutate(crime_index = 0.5 * homicide_shootings_per_1000_res_index +
0.5 * other_violent_crime_index,
crime_grime_index = (2/3) * crime_index + (1/3) * grime_index,
crime_grime_index_quadrature = sqrt(crime_index^2 + grime_index^2))
Now creating the “grime” index:
\[ GrimeIndex = \frac{1}{3}(IllegalDumpingIndex + PropMaintenanceIndex + RightOfWayIndex) \] Combine into a single index. Crime weighted more heavily.
\[ CrimeGrimeIndex = \frac{2}{3}CrimeIndex + \frac{1}{3}GrimeIndex \]
Just Show Me The Results
# base_map <- get_map(
# location=c(lon = -76.61, lat = 39.3),
# zoom=12,
# maptype = 'toner-lite',
# source = 'stamen'
# )
#
# ggmap(base_map) +
# # geom_point(
# # data = repeat_offender_locations %>%
# # filter(!is.na(lon),
# # n >= 3),
# # aes(x = lon, y = lat),
# # color = "blue",
# # size = 2
# # )
# geom_polygon(
# data = fortify(hood_metrics_over150) %>%
# filter(!is.na(crime_grime_index)),
# aes(x = long, y = lat, group = group, fill = crime_grime_index),
# alpha = 0.9,color = "black"
# )# +
# # scale_fill_viridis_c(na.value = NA,
# # #values = rescale(c(0, 20, 70), c(0,1)),
# # option = "plasma",
# # guide_colorbar(title = "Annual DV Incidents\nper 1,000 Residents"))
# #
hoods@data <- hoods@data %>%
left_join(
hood_metrics_over150@data %>% select(-ACS_2013_2017),
by = "label"
)
# color palette for map
pal <- colorNumeric("viridis",
domain = hoods$crime_grime_index,
na.color = NA)
# labels
hoods@data <- hoods@data %>%
mutate(map_label = paste0(
"<b>",hoods$label, "</b><br>",
"Population: ", round(hoods$ACS_2013_2017, 0), "<br>",
"Crime & Grime Index: ", round(hoods$crime_grime_index, 2), "<br>",
"Crime Index: ", round(hoods$crime_index, 2), "<br>",
"Grime Index: ", round(hoods$grime_index, 2), "<br>",
"<br><b>2019 Crime</b><br>",
"Homicides + Shootings: ", hoods$homicide_shootings, "<br>",
"Rape: ", hoods$rape, "<br>",
"Aggravated Assault: ", hoods$agg_assault, "<br>",
"Robbery: ", hoods$robbery, "<br>",
"<br><b>2019 Service Requests</b><br>",
"Illegal Dumping SRs: ", hoods$illegal_dumping, "<br>",
"Property Maintenance SRs: ", hoods$property_maintenance, "<br>",
"Right-of-Way SRs: ", hoods$right_of_way, "<br>"
))
leaflet() %>%
setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
addProviderTiles(providers$Stamen.TonerLite) %>%
addPolygons(data = hoods,
weight = 2,
fillColor = ~pal(hoods$crime_grime_index),
color = "black",
opacity = 0.9,
fillOpacity = 0.75,
label = ~lapply(hoods$map_label, HTML)) %>%
addControl("Baltimore<br>Crime & Grime Index", position = "topleft")
Distribution of Indexes
Because of the homicide and shooting index being 1 for neighborhoods with at least one homicide or shooting and 0 for all others, the distribution is bifurcated into neighborhoods with and without shootings.
hoods@data %>%
ggplot(aes(crime_grime_index)) +
geom_histogram() +
theme_iteam_google_docs() +
geom_text(aes(0.3, 25,
label="At Least 1 Homicide or Shooting\nin 2019"), size = 2, hjust = 0) +
geom_text(aes(0.04, 25,
label="No Homicides or Shootings\nin 2019"), size = 2, hjust = 0)

The List, In Order of Descending Crime-Grime Index
Some neighborhoods with very small populations still sneak onto the list because we only used 150 as the cut-off, but those can be manually dropped if not interested.
the_list <- hoods@data %>%
arrange(desc(crime_grime_index)) %>%
select(-acres, -nbrdesc, -shape_leng, -shape_area, -color_2, -Decennial2010) %>%
rename(neighborhood = label,
population_acs_2013_2017 = ACS_2013_2017)
write_csv(the_list, "../data/processed/crime_grime_index_v3.csv")
the_list
top_20 <- hoods %>%
arrange(desc(crime_grime_index)) %>%
top_n(n = 20, wt = crime_grime_index)
leaflet() %>%
setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
addProviderTiles(providers$Stamen.TonerLite) %>%
addPolygons(data = cityline,
weight = 2,
#fillColor = iteam.colors[1],
color = iteam.colors[2],
opacity = 0.9,
fillOpacity = 0.0) %>%
addPolygons(data = top_20,
weight = 2,
fillColor = iteam.colors[1],
color = iteam.colors[2],
opacity = 0.9,
fillOpacity = 0.75,
popup = ~lapply(top_20$map_label, HTML),
label = top_20$label,
labelOptions = labelOptions(noHide = T,
textOnly = T,
direction = 'center',
textsize = 2)) %>%
addControl("Baltimore<br>Crime & Grime Index<br>Top 20 Neighborhoods<br><br>Click Neighborhood<br>For More Info",
position = "topleft")
LS0tCnRpdGxlOiAiQ3JpbWUgKyBHcmltZSBJbmRleCBEZXZlbG9wbWVudCIKYXV0aG9yOiAiSnVzdGluIEVsc3phc3oiCmVtYWlsOiAianVzdGluLmVsc3phc3pAYmFsdGltb3JlY2l0eS5nb3YiCmRhdGU6ICJUdWVzZGF5LCBKYW51YXJ5IDIxLCAyMDIwIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgZmlnX2hlaWdodDogNQogICAgZmlnX3dpZHRoOiAxMAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMQplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KClRoZSBwdXJwb3NlIG9mIHRoaXMgbm90ZWJvb2sgaXMgdG8gY3JlYXRlIGEgbmVpZ2hib3Job29kIGluZGV4IHRoYXQgY29tYmluZXMgY3JpbWUgYW5kIGNsZWFubGluZXNzIGRhdGEuIFRoZSBpbmRleCByZWxpZXMgb24gWzMxMSBzZXJ2aWNlIHJlcXVlc3QgZGF0YV0oaHR0cHM6Ly9kYXRhLmJhbHRpbW9yZWNpdHkuZ292L0NpdHktU2VydmljZXMvMzExLUN1c3RvbWVyLVNlcnZpY2UtUmVxdWVzdHMvOWFndy1zeHNyKSBhbmQgW1BhcnQgMSB2aW9sZW50IGNyaW1lIGRhdGFdKGh0dHBzOi8vZGF0YS5iYWx0aW1vcmVjaXR5Lmdvdi9QdWJsaWMtU2FmZXR5L0JQRC1QYXJ0LTEtVmljdGltLUJhc2VkLUNyaW1lLURhdGEvd3NmcS1tdmlqKSwgYm90aCBhdmFpbGJsZSBvbiBPcGVuIEJhbHRpbW9yZS4KCiMgQnJpZWYgRGVzY3JpcHRpb24gb2YgVGhlIEluZGV4CgpUaGUgaW5kZXggcHJpbWFyaWx5IHJlbGllcyBvbiBjb3VudHMgb2YgdmlvbGVudCBjcmltZSBhbmQgc2VydmljZSByZXF1ZXN0cyAqcGVyIDEsMDAwIHJlc2lkZW50cyouIFRoaXMgaXMgZG9uZSB0byByZW1vdmUgdGhlIGluZmx1ZW5jZSBvZiBwb3B1bGF0aW9uIG9uIHRoZSBjb3VudHMgb2YgdGhlc2UgZXZlbnRzLiBUaGUgcGVyLWNhcGl0YSBtZXRyaWNzIGFyZSB0aGVuIHNjYWxlZCB0byBiZXR3ZWVuIDAgYW5kIDEgdG8gYnVpbGQgdXAgdGhlIG92ZXJhbGwgaW5kZXguCgpBbGwgUGFydCAxIGNyaW1lIHR5cGVzIGFyZSB1c2VkLgoKVGhlIFNSIHR5cGVzIHVzZWQgYXJlOgoKSWxsZWdhbCBEdW1waW5nOgoKLSBIQ0QtSWxsZWdhbCBEdW1waW5nCi0gU1ctU0lVIENsZWFuIFVwCgpQcm9wZXJ0eSBNYWludGVuYW5jZToKCi0gSENELVNhbml0YXRpb24gUHJvcGVydHkKLSBTVy1Cb2FyZGluZwotIFNXLUhHVwotIFNXLUNsZWFuaW5nCgpSaWdodC1vZi1XYXkgQ2xlYW5pbmcKCi0gU1ctRGlydHkgU3RyZWV0Ci0gU1ctRGlydHkgQWxsZXkKCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IFQsIGVjaG8gPSBULCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlID0gVFJVRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBULCAKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGLAogICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZSA9IFQsCiAgICAgICAgICAgICAgICAgICAgICBmaWcud2lkdGggPSA0LAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDMpCmBgYAoKYGBge3IgbGlicmFyaWVzfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ2l0ZWFtKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoc3BkcGx5cikKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KFJTb2NyYXRhKQpsaWJyYXJ5KGdlb2pzb25zZikKbGlicmFyeShnZ21hcCkKbGlicmFyeShyZ2RhbCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoc2YpCmxpYnJhcnkoaHRtbHRvb2xzKQoKCnJlZ2lzdGVyX2dvb2dsZSgiQUl6YVN5Qnl5S1VqNFFhQWx6TnRQSlB3LUhrRUVyN2Q0aFRhd08wIikKYGBgCiAKYGBge3IgbG9hZF9zciwgY2FjaGUgPSBUfQpzcl9xdWVyeSA8LSBwYXN0ZTAoImh0dHBzOi8vZGF0YS5iYWx0aW1vcmVjaXR5Lmdvdi9yZXNvdXJjZS85YWd3LXN4c3IuanNvbiIsCiAgICAgICAgICAgICAgICAiPyR3aGVyZT0iLAogICAgICAgICAgICAgICAgIigoc3J0eXBlIGxpa2UgJ1NXLUJvYXJkaW5nJyBPUiAiLAogICAgICAgICAgICAgICAgInNydHlwZSBsaWtlICdTVy1DbGVhbmluZycgT1IgIiwKICAgICAgICAgICAgICAgICJzcnR5cGUgbGlrZSAnU1ctSEdXJyBPUiAiLAogICAgICAgICAgICAgICAgInNydHlwZSBsaWtlICdTVy1EaXJ0eSBBbGxleScgT1IgIiwKICAgICAgICAgICAgICAgICJzcnR5cGUgbGlrZSAnU1ctRGlydHkgU3RyZWV0JyBPUiAiLAogICAgICAgICAgICAgICAgInNydHlwZSBsaWtlICdIQ0QtSWxsZWdhbCBEdW1waW5nJyBPUiAiLAogICAgICAgICAgICAgICAgInNydHlwZSBsaWtlICdIQ0QtU0lVJyBPUiAiLAogICAgICAgICAgICAgICAgInNydHlwZSBsaWtlICdTVy1TSVUgQ2xlYW4gVXAnIE9SICIsCiAgICAgICAgICAgICAgICAic3J0eXBlIGxpa2UgJ0hDRC1TYW5pdGF0aW9uIFByb3BlcnR5JykgQU5EICIsCiAgICAgICAgICAgICAgICAiZGF0ZV9leHRyYWN0X3koY3JlYXRlZGRhdGUpPT0yMDE5KSIpCgpzciA8LSByZWFkLnNvY3JhdGEodXJsID0gc3JfcXVlcnkpCgpzciA8LSBzciAlPiUgCiAgbXV0YXRlKAogICAgbGF0aXR1ZGUgPSBhcy5udW1lcmljKGxhdGl0dWRlKSwgCiAgICBsb25naXR1ZGUgPSBhcy5udW1lcmljKGxvbmdpdHVkZSksCiAgICBuZWlnaGJvcmhvb2QgPSB0b3VwcGVyKG5laWdoYm9yaG9vZCkpCmBgYAoKYGBge3IgbG9hZF9jcmltZSwgY2FjaGUgPSBUfQpjcmltZV9xdWVyeSA8LSBwYXN0ZTAoImh0dHBzOi8vZGF0YS5iYWx0aW1vcmVjaXR5Lmdvdi9yZXNvdXJjZS93c2ZxLW12aWouanNvbj8kd2hlcmU9IiwKICAgICAgICAgICAgICAgICIoRGVzY3JpcHRpb24gbGlrZSAnSE9NSUNJREUnIE9SICIsCiAgICAgICAgICAgICAgICAiRGVzY3JpcHRpb24gbGlrZSAnU0hPT1RJTkcnIE9SICIsIAogICAgICAgICAgICAgICAgIkRlc2NyaXB0aW9uIGxpa2UgJ0FHRy4gQVNTQVVMVCcgT1IgIiwKICAgICAgICAgICAgICAgICJEZXNjcmlwdGlvbiBsaWtlICdSQVBFJyBPUiAiLAogICAgICAgICAgICAgICAgImNvbnRhaW5zKERlc2NyaXB0aW9uLCAnUk9CQkVSWScpKSBBTkQgIiwKICAgICAgICAgICAgICAgICJkYXRlX2V4dHJhY3RfeShjcmltZWRhdGUpPT0yMDE5IikKCmNyaW1lIDwtIHJlYWQuc29jcmF0YShjcmltZV9xdWVyeSkKYGBgCgpgYGB7ciBjaXR5bGluZX0KY2l0eWxpbmVfdXJsIDwtICJodHRwczovL2RhdGEuYmFsdGltb3JlY2l0eS5nb3YvcmVzb3VyY2UvbmZ5Yy1wZWp4Lmdlb2pzb24iCgojIHN1cmVseSB0aGVyZSBpcyBhIGJldHRlciB3YXksIGFuZCBpIHRyaWVkLCBidXQgY291bGRuJ3QgZ2V0IGFueXRoaW5nIGVsc2UgdG8gd29yay4KIyBmcm9tIGdlb2pzb24gdG8gc2YgdG8gc3BhdGlhbCBkZi4gZ2VvanNvbl9zcCBkaWRuJ3Qgd29yayBmb3IgbWUuCmNpdHlsaW5lIDwtIGdlb2pzb25fc2YoY2l0eWxpbmVfdXJsKQpjaXR5bGluZSA8LSBhc19TcGF0aWFsKGNpdHlsaW5lKQpjaXR5bGluZSA8LSBzcFRyYW5zZm9ybShjaXR5bGluZSwgQ1JTKCIraW5pdD1lcHNnOjQzMjYiKSkKCmBgYAoKCmBgYHtyIGxvYWRfbmVpZ2hib3Job29kc30KaG9vZHMudXJsIDwtICJodHRwczovL2RhdGEuYmFsdGltb3JlY2l0eS5nb3YvcmVzb3VyY2UvaDNmeC01NHEzLmdlb2pzb24iCgojIHN1cmVseSB0aGVyZSBpcyBhIGJldHRlciB3YXksIGFuZCBpIHRyaWVkLCBidXQgY291bGRuJ3QgZ2V0IGFueXRoaW5nIGVsc2UgdG8gd29yay4KIyBmcm9tIGdlb2pzb24gdG8gc2YgdG8gc3BhdGlhbCBkZi4gZ2VvanNvbl9zcCBkaWRuJ3Qgd29yayBmb3IgbWUuCmhvb2RzIDwtIGdlb2pzb25fc2YoaG9vZHMudXJsKQpob29kcyA8LSBhc19TcGF0aWFsKGhvb2RzKQpob29kcyA8LSBzcFRyYW5zZm9ybShob29kcywgQ1JTKCIraW5pdD1lcHNnOjQzMjYiKSkKYGBgCgpgYGB7cn0KIyBqb2luIGFjcyBkYXRhIHRvIGhvb2RzCmhvb2RzX3BvcCA8LSByZWFkX2V4Y2VsKCIuLi9kYXRhL3Jhdy9OZWlnaGJvcmhvb2QgUG9wLnhsc3giKQpob29kc0BkYXRhIDwtIGxlZnRfam9pbihob29kc0BkYXRhLCBob29kc19wb3AsIGJ5ID0gYygibGFiZWwiID0gIk5hbWUiKSkKYGBgCgoKYGBge3J9CiMgYnBkIGZhY2lsaXRpZXMgdG8gcmVtb3ZlCmZhY2lsaXRpZXMgPC0gYygKICAiMzAwIEUgTUFESVNPTiBTVCIsICMgY2VudHJhbCBib29raW5nCiAgIjE0MDAgRSBOT1JUSCBBVkUiLCAjIGRpc3RyaWN0IGNvdXJ0CiAgIjE5MDAgQVJHT05ORSBEUiIsICMgbm9ydGhlYXN0ZXJuCiAgIjIyMDAgVyBDb2xkIFNwcmluZyBMbiIsICMgbm9ydGhlcm4KICAiMTAwMCBOIE1PVU5UIFNUIiwgIyB3ZXN0ZXJuCiAgIjUyMDAgUkVJU1RFUlNUT1dOIFJECSIsICMgbm9ydGh3ZXN0CiAgIjE2MDAgRURJU09OIEhXWSIsICMgZWFzdGVybgogICI0MDAgRk9OVEhJTEwgQVZFIiwgIyBzb3V0aHdlc3QKICAiMCBDSEVSUlkgSElMTCBSRCIsICMgc291dGhlcm4KICAiNjAwIEUgRkFZRVRURSBTVCIsICMgY2VudHJhbC9ocQogICI1NzAwIEVBU1RFUk4gQVZFIiwgIyBzb3V0aGVhc3QKICAiMjAwMCBXIEJBTFRJTU9SRSBTVCIsICAjIGJvbiBzZWNvdXJzCiAgIjQwMDAgREVFUFdPT0QgUkQiLCAjIGxvY2ggcmF2ZW4gdmEKICAiMzAwIE4gR0FZIFNUIiwgIyBqdXYgYm9va2luZwogICI0OTAwIEVBU1RFUk4gQVYiLCAjIGJheXZpZXcKICAiMTgwMCBPUkxFQU5TIFNUIiwgIyBqaGggZG93bnRvd24KICAiNjAwIE4gV09MRkUgU1QiLCAjIGpoaCBkb3dudG93bgogICIwIFMgR1JFRU5FIFNUIiwgIyB1bWQgbWVkaWNhbCBjZW50ZXIKICAiMzQwMCBOIENBTFZFUlQgU1QiICMgdW5pb24gbWVtb3JpYWwKKQoKYGBgCgpQb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbiBvZiBCYWx0aW1vcmUgbmVpZ2hib3Job29kcy4KCmBgYHtyIGZpZy5oZWlnaHQgPSAxLCBmaWcud2lkdGggPSAyfQpob29kc0BkYXRhICU+JQogIGdncGxvdChhZXMoQUNTXzIwMTNfMjAxNykpICsKICBnZW9tX2hpc3RvZ3JhbSgpICsKICB0aGVtZV9pdGVhbV9nb29nbGVfZG9jcygpCmBgYAoKVGVudGggcGVyY2VudGlsZSBmb3IgcG9wdWxhdGlvbjoKCmBgYHtyfQpxdWFudGlsZShob29kcyRBQ1NfMjAxM18yMDE3LCAwLjEsIG5hLnJtID0gVCkKYGBgCgojIEFuYWx5c2lzCgpgYGB7cn0KY3JpbWVfbGFiZWxlZCA8LSBjcmltZSAlPiUKICBmaWx0ZXIoCiAgICAhaXMubmEobGF0aXR1ZGUpLAogICAgIShsb2NhdGlvbiAlaW4lIGZhY2lsaXRpZXMpCiAgICApICU+JQogIHN0X2FzX3NmKGNvb3JkcyA9IGMoImxvbmdpdHVkZSIsICJsYXRpdHVkZSIpLCBjcnMgPSA0MzI2KSAlPiUKICBzdF9qb2luKHN0X2FzX3NmKGhvb2RzKSwgam9pbiA9IHN0X3dpdGhpbiwgbGVmdCA9IEYpCgpjcmltZV9ob29kX2NvdW50cyA8LSBjcmltZV9sYWJlbGVkICU+JQogIGRhdGEuZnJhbWUoKSAlPiUKICBjb3VudChkZXNjcmlwdGlvbiwgbGFiZWwpICU+JQogIHNwcmVhZChrZXkgPSBkZXNjcmlwdGlvbiwgdmFsdWUgPSBuKSAlPiUKICByZXBsYWNlKGlzLm5hKC4pLCAwKSAlPiUKICB0cmFuc211dGUoCiAgICBsYWJlbCA9IGxhYmVsLAogICAgYWdnX2Fzc2F1bHQgPSBgQUdHLiBBU1NBVUxUYCwKICAgIHJhcGUgPSBSQVBFLAogICAgaG9taWNpZGVfc2hvb3RpbmdzID0gSE9NSUNJREUgKyBTSE9PVElORywKICAgIHJvYmJlcnkgPSBgUk9CQkVSWSAtIENPTU1FUkNJQUxgICsKICAgICAgYFJPQkJFUlkgLSBDQVJKQUNLSU5HYCArCiAgICAgIGBST0JCRVJZIC0gUkVTSURFTkNFYCArCiAgICAgIGBST0JCRVJZIC0gU1RSRUVUYCkgCmBgYAoKYGBge3J9CnNyX2xhYmVsZWQgPC0gc3IgJT4lCiAgZmlsdGVyKAogICAgIWlzLm5hKGxhdGl0dWRlKQogICAgKSAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJsb25naXR1ZGUiLCAibGF0aXR1ZGUiKSwgY3JzID0gNDMyNikgJT4lCiAgc3Rfam9pbihzdF9hc19zZihob29kcyksIGpvaW4gPSBzdF93aXRoaW4sIGxlZnQgPSBGKQoKc3JfaG9vZF9jb3VudHMgPC0gc3JfbGFiZWxlZCAlPiUKICBkYXRhLmZyYW1lKCkgJT4lCiAgY291bnQoc3J0eXBlLCBsYWJlbCkgJT4lCiAgc3ByZWFkKGtleSA9IHNydHlwZSwgdmFsdWUgPSBuKSAlPiUKICByZXBsYWNlKGlzLm5hKC4pLCAwKSAlPiUKICB0cmFuc211dGUoCiAgICBsYWJlbCA9IGxhYmVsLAogICAgaWxsZWdhbF9kdW1waW5nID0gYEhDRC1JbGxlZ2FsIER1bXBpbmdgICsgYFNXLVNJVSBDbGVhbiBVcGAsCiAgICBwcm9wZXJ0eV9tYWludGVuYW5jZSA9IGBIQ0QtU2FuaXRhdGlvbiBQcm9wZXJ0eWAgKwogICAgICBgU1ctQm9hcmRpbmdgICsKICAgICAgYFNXLUNsZWFuaW5nYCArCiAgICAgIGBTVy1IR1dgLAogICAgcmlnaHRfb2Zfd2F5ID0gYFNXLURpcnR5IFN0cmVldGAgKyBgU1ctRGlydHkgQWxsZXlgCiAgKQoKYGBgCgpgYGB7cn0KIyB0byBwZXIgMSwwMDAgcmVzaWRlbnRzCnRvX3Blcl9jYXBpdGEgPC0gZnVuY3Rpb24oeCl7MTAwMCAqIHggLyBob29kcyRBQ1NfMjAxM18yMDE3fQoKaG9vZF9tZXRyaWNzIDwtIGhvb2RzCgpob29kX21ldHJpY3NAZGF0YSA8LSBob29kX21ldHJpY3NAZGF0YSAlPiUKICBzZWxlY3QoLWFjcmVzLAogICAgICAgICAtbmJyZGVzYywKICAgICAgICAgLXNoYXBlX2xlbmcsCiAgICAgICAgIC1zaGFwZV9hcmVhLAogICAgICAgICAtY29sb3JfMiwKICAgICAgICAgLURlY2VubmlhbDIwMTApCgpob29kX21ldHJpY3NAZGF0YSA8LSBob29kX21ldHJpY3NAZGF0YSAlPiUKICBsZWZ0X2pvaW4oCiAgICBjcmltZV9ob29kX2NvdW50cywKICAgIGJ5ID0gImxhYmVsIikgJT4lCiAgbGVmdF9qb2luKAogICAgc3JfaG9vZF9jb3VudHMsCiAgICBieSA9ICJsYWJlbCIKICApICU+JQogIG11dGF0ZV9hdCgKICAgIHZhcnMoCiAgICAgIGlsbGVnYWxfZHVtcGluZywKICAgICAgcHJvcGVydHlfbWFpbnRlbmFuY2UsCiAgICAgIHJpZ2h0X29mX3dheSwKICAgICAgaG9taWNpZGVfc2hvb3RpbmdzLAogICAgICByb2JiZXJ5LAogICAgICBhZ2dfYXNzYXVsdCwKICAgICAgcmFwZSksCiAgICBsaXN0KHBlcl8xMDAwX3JlcyA9IHRvX3Blcl9jYXBpdGEpKQpgYGAKCkRpc3RyaWJ1dGlvbiBvZiBwZXIgY2FwaXRhIHJhdGVzIG9mIGNyaW1lIGFuZCBzZXJ2aWNlIHJlcXVlc3RzOgoKYGBge3J9Cmhvb2RfbWV0cmljc0BkYXRhICU+JQogIHNlbGVjdCgKICAgIGxhYmVsLAogICAgaWxsZWdhbF9kdW1waW5nX3Blcl8xMDAwX3JlcywKICAgIHByb3BlcnR5X21haW50ZW5hbmNlX3Blcl8xMDAwX3JlcywKICAgIHJpZ2h0X29mX3dheV9wZXJfMTAwMF9yZXMsCiAgICBob21pY2lkZV9zaG9vdGluZ3NfcGVyXzEwMDBfcmVzLAogICAgcm9iYmVyeV9wZXJfMTAwMF9yZXMsCiAgICBhZ2dfYXNzYXVsdF9wZXJfMTAwMF9yZXMsCiAgICByYXBlX3Blcl8xMDAwX3JlcwogICkgJT4lCiAgcGl2b3RfbG9uZ2VyKC1sYWJlbCkgJT4lCiAgZ2dwbG90KGFlcyh2YWx1ZSkpICsKICBmYWNldF93cmFwKH5uYW1lLCBzY2FsZXMgPSAiZnJlZSIpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTApICsKICB0aGVtZV9pdGVhbV9nb29nbGVfZG9jcygpICsKICBsYWJzKHRpdGxlID0gIjIwMTkgRXZlbnRzIHBlciAxLDAwMCBSZXNpZGVudHMiKQpgYGAKCkluIGFsbCBjYXNlcywgb3V0bGllciBuZWlnaGJvcmhvb2RzIHdpdGggdmVyeSBzbWFsbCBwb3B1bGF0aW9ucyBza2V3IGRhdGEuIAoKVHJ5IHJlbW92aW5nIG5laWdoYm9yaG9vZHMgd2l0aCBsZXNzIHRoYW4gMTUwIHJlc2lkZW50cyAoMTAlIG9mIG5laWdoYm9yaG9vZHMpLiBUaGVzZSBhcmUgdHlwaWNhbGx5IHBhcmtzLCBpbmR1c3RyaWFsIGFyZWFzLCBldGMuCgpgYGB7cn0KaG9vZF9tZXRyaWNzQGRhdGEgJT4lCiAgZmlsdGVyKEFDU18yMDEzXzIwMTcgPj0gMTUwKSAlPiUKICBzZWxlY3QoCiAgICBsYWJlbCwKICAgIGlsbGVnYWxfZHVtcGluZ19wZXJfMTAwMF9yZXMsCiAgICBwcm9wZXJ0eV9tYWludGVuYW5jZV9wZXJfMTAwMF9yZXMsCiAgICByaWdodF9vZl93YXlfcGVyXzEwMDBfcmVzLAogICAgaG9taWNpZGVfc2hvb3RpbmdzX3Blcl8xMDAwX3JlcywKICAgIHJvYmJlcnlfcGVyXzEwMDBfcmVzLAogICAgYWdnX2Fzc2F1bHRfcGVyXzEwMDBfcmVzLAogICAgcmFwZV9wZXJfMTAwMF9yZXMKICApICU+JQogIHBpdm90X2xvbmdlcigtbGFiZWwpICU+JQogIGdncGxvdChhZXModmFsdWUpKSArCiAgZmFjZXRfd3JhcCh+bmFtZSwgc2NhbGVzID0gImZyZWUiKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUwKSArCiAgICB0aGVtZV9pdGVhbV9nb29nbGVfZG9jcygpICsKICBsYWJzKHRpdGxlID0gIjIwMTkgRXZlbnRzIHBlciAxLDAwMCBSZXNpZGVudHNcbk5laWdoYm9yaG9vZHMgd2l0aCA+PSAxNTAgUmVzaWRlbnRzIikKYGBgCgpHb2luZyBmb3J3YXJkLCByZW1vdmluZyBuZWlnaGJvcmhvb2RzIHdpdGggbGVzcyB0aGFuIDE1MCByZXNpZGVudHMuCgpgYGB7cn0KaG9vZF9tZXRyaWNzX292ZXIxNTAgPC0gc3Vic2V0KGhvb2RfbWV0cmljcywgaG9vZF9tZXRyaWNzJEFDU18yMDEzXzIwMTcgPiAxNTApCmhvb2RfbWV0cmljc19vdmVyMTUwIDwtIHN1YnNldChob29kX21ldHJpY3Nfb3ZlcjE1MCwgIWdyZXBsKCJJTkRVU1RSSUFMIiwgbGFiZWwsIGlnbm9yZS5jYXNlID0gVCkpCmBgYAoKTnVtYmVyIG9mIG5laWdoYm9yaG9vZHMgd2l0aCBhdCBsZWFzdCBvbmUgaG9taWNpZGUgb3Igc2hvb3Rpbmc6CgpgYGB7cn0KaG9vZF9tZXRyaWNzX292ZXIxNTBAZGF0YSAlPiUKICBjb3VudChob21pY2lkZV9zaG9vdGluZ3MgPj0gMSkKYGBgCgpXaWxsIGxpa2VseSBhc3NpZ24gMSB0byBuZWlnaGJvcmhvb2RzIHdpdGggYXQgbGVhc3QgMSBob21pY2lkZSBvciBzaG9vdGluZywgMCB0byBhbGwgb3RoZXJzLiBUaGlzIHdpbGwgZW5zdXJlIG5laWdoYm9yaG9vZHMgd2l0aCBndW4gdmlvbGVuY2UgYXJlIGdpdmVuIHByZWNlZGVuY2UuCgojIEJ1aWxkaW5nIFRoZSBJbmRleAoKQWxsIHBhcmFtZXRlcnMgd2lsbCBiZSBzY2FsZWQgdG8gYmV0d2VlbiAwIGFuZCAxIGJlY2F1c2UgdmFyaWFibGVzIGFyZSBvbiBkaWZmZXJlbnQgc2NhbGVzLgoKYGBge3J9CiMgc2NhbGUgcGVyIGNhcGl0YQpzY2FsZV9pbmRleCA8LSBmdW5jdGlvbih4KXtyZXNjYWxlKHgsIHRvID0gYygwLDEpKX0KCmhvb2RfbWV0cmljc19vdmVyMTUwQGRhdGEgPC0gaG9vZF9tZXRyaWNzX292ZXIxNTBAZGF0YSAlPiUKICBtdXRhdGUoCiAgICBob21pY2lkZV9zaG9vdGluZ3NfaW5kZXggPSBpZmVsc2UoaG9taWNpZGVfc2hvb3RpbmdzID4gMCwgMSwgMCksCiAgICByYXBlX2luZGV4ID0gaWZlbHNlKHJhcGUgPiAwLCAxLCAwKSkgJT4lCiAgbXV0YXRlX2F0KAogICAgdmFycygKICAgICAgaWxsZWdhbF9kdW1waW5nX3Blcl8xMDAwX3JlcywKICAgICAgcHJvcGVydHlfbWFpbnRlbmFuY2VfcGVyXzEwMDBfcmVzLAogICAgICByaWdodF9vZl93YXlfcGVyXzEwMDBfcmVzLAogICAgICBob21pY2lkZV9zaG9vdGluZ3NfcGVyXzEwMDBfcmVzLAogICAgICByb2JiZXJ5X3Blcl8xMDAwX3JlcywKICAgICAgYWdnX2Fzc2F1bHRfcGVyXzEwMDBfcmVzLAogICAgICByYXBlX3Blcl8xMDAwX3JlcyksCiAgICBsaXN0KGluZGV4ID0gc2NhbGVfaW5kZXgpCiAgICApIApgYGAKCmBgYHtyfQojIHBsb3QgcmVzY2FsZWQgZGF0YQpob29kX21ldHJpY3Nfb3ZlcjE1MEBkYXRhICU+JQogIHNlbGVjdCgKICAgIGxhYmVsLAogICAgaWxsZWdhbF9kdW1waW5nX3Blcl8xMDAwX3Jlc19pbmRleCwKICAgIHByb3BlcnR5X21haW50ZW5hbmNlX3Blcl8xMDAwX3Jlc19pbmRleCwKICAgIHJpZ2h0X29mX3dheV9wZXJfMTAwMF9yZXNfaW5kZXgsCiAgICBob21pY2lkZV9zaG9vdGluZ3NfcGVyXzEwMDBfcmVzX2luZGV4LAogICAgcm9iYmVyeV9wZXJfMTAwMF9yZXNfaW5kZXgsCiAgICBhZ2dfYXNzYXVsdF9wZXJfMTAwMF9yZXNfaW5kZXgsCiAgICByYXBlX3Blcl8xMDAwX3Jlc19pbmRleAogICkgJT4lCiAgcGl2b3RfbG9uZ2VyKC1sYWJlbCkgJT4lCiAgZ2dwbG90KGFlcyh2YWx1ZSkpICsKICBmYWNldF93cmFwKH5uYW1lLCBzY2FsZXMgPSAiZnJlZSIpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTApICsKICAgIHRoZW1lX2l0ZWFtX2dvb2dsZV9kb2NzKCkgKwogIGxhYnModGl0bGUgPSAiU3RhbmRhcmRpemVkIChiZXR3ZWVuIDAgYW5kIDEpIDIwMTkgRXZlbnRzIHBlciAxLDAwMCBSZXNpZGVudHNcbk5laWdoYm9yaG9vZHMgd2l0aCA+PSAxNTAgUmVzaWRlbnRzIikKYGBgCgoqKkFnYWluLCBhbGwgbWV0cmljcyBoYXZlIGJlZW4gc2NhbGVkIHRvIGJldHdlZW4gMCBhbmQgMS4qKiBDb21iaW5lIGNyaW1lIG1ldHJpY3MgaW50byBhIGhvbWljaWRlIGFuZCBzaG9vdGluZyBwYXJhbWV0ZXIgKDAgaWYgbm9uZSBpbiBuZWlnaGJvcmhvb2QsIDEgaWYgYXQgbGVhc3QgMSkgYW5kIGFuICJvdGhlciB2aW9sZW50IGNyaW1lIiBwYXJhbWV0ZXIgd2hlcmU6CgokJCBIb21pY2lkZVNob290aW5nc0luZGV4ID0gXGJlZ2lue2Nhc2VzfQoxICYgXHRleHR7aWYgJE5fe2hvbWljaWRlc30rTl97c2hvb3RpbmdzfSA+IDAkIH0gXFwgCjAgJiBcdGV4dHtvdGhlcndpc2V9IFxlbmR7Y2FzZXN9JCQKCiQkIE90aGVyVmlvbGVudENyaW1lSW5kZXggPSBcZnJhY3sxfXszfShSYXBlSW5kZXgrIEFnZ0Fzc2F1bHRJbmRleCArIFJvYmJlcnlJbmRleCkgJCQKCkNvbWJpbmluZyB0aGUgdHdvIGNyaW1lIGluZGljZXM6CgokJCBDcmltZUluZGV4ID0gXGZyYWN7MX17Mn0gKEhvbWljaWRlU2hvb3RpbmdzSW5kZXggKyBPdGhlclZpb2xlbnRDcmltZUluZGV4KSAkJApgYGB7cn0KIyBjcmVhdGUgY29tYmluZWQgaW5kZXgKaG9vZF9tZXRyaWNzX292ZXIxNTBAZGF0YSA8LSBob29kX21ldHJpY3Nfb3ZlcjE1MEBkYXRhICU+JQogIHJvd3dpc2UoKSAlPiUKICBtdXRhdGUoCiAgICBvdGhlcl92aW9sZW50X2NyaW1lX2luZGV4ID0gCiAgICAgIG1lYW4oCiAgICAgICAgYyhyYXBlX3Blcl8xMDAwX3Jlc19pbmRleCwKICAgICAgICAgIGFnZ19hc3NhdWx0X3Blcl8xMDAwX3Jlc19pbmRleCwgCiAgICAgICAgICByb2JiZXJ5X3Blcl8xMDAwX3Jlc19pbmRleCksIAogICAgICAgIG5hLnJtID0gVCksCiAgICBncmltZV9pbmRleCA9IG1lYW4oCiAgICAgIGMoaWxsZWdhbF9kdW1waW5nX3Blcl8xMDAwX3Jlc19pbmRleCwKICAgICAgICBwcm9wZXJ0eV9tYWludGVuYW5jZV9wZXJfMTAwMF9yZXNfaW5kZXgsCiAgICAgICAgcmlnaHRfb2Zfd2F5X3Blcl8xMDAwX3Jlc19pbmRleCksCiAgICAgIG5hLnJtID0gVCkKICApICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoY3JpbWVfaW5kZXggPSAwLjUgKiBob21pY2lkZV9zaG9vdGluZ3NfcGVyXzEwMDBfcmVzX2luZGV4ICsgCiAgICAgICAgICAgMC41ICogb3RoZXJfdmlvbGVudF9jcmltZV9pbmRleCwKICAgICAgICAgY3JpbWVfZ3JpbWVfaW5kZXggPSAoMi8zKSAqIGNyaW1lX2luZGV4ICsgKDEvMykgKiBncmltZV9pbmRleCwKICAgICAgICAgY3JpbWVfZ3JpbWVfaW5kZXhfcXVhZHJhdHVyZSA9IHNxcnQoY3JpbWVfaW5kZXheMiArIGdyaW1lX2luZGV4XjIpKQpgYGAKCk5vdyBjcmVhdGluZyB0aGUgImdyaW1lIiBpbmRleDoKCiQkIEdyaW1lSW5kZXggPSBcZnJhY3sxfXszfShJbGxlZ2FsRHVtcGluZ0luZGV4ICsgUHJvcE1haW50ZW5hbmNlSW5kZXggKyBSaWdodE9mV2F5SW5kZXgpICQkCkNvbWJpbmUgaW50byBhIHNpbmdsZSBpbmRleC4gQ3JpbWUgd2VpZ2h0ZWQgbW9yZSBoZWF2aWx5LgoKJCQgQ3JpbWVHcmltZUluZGV4ID0gXGZyYWN7Mn17M31DcmltZUluZGV4ICsgXGZyYWN7MX17M31HcmltZUluZGV4ICQkCgojIEp1c3QgU2hvdyBNZSBUaGUgUmVzdWx0cwoKYGBge3IgZmlnLmhlaWdodCA9IDgsIGZpZy53aWR0aCA9IDh9CiMgYmFzZV9tYXAgPC0gZ2V0X21hcCgKIyAgIGxvY2F0aW9uPWMobG9uID0gLTc2LjYxLCBsYXQgPSAzOS4zKSwgCiMgICB6b29tPTEyLCAKIyAgIG1hcHR5cGUgPSAndG9uZXItbGl0ZScsIAojICAgc291cmNlID0gJ3N0YW1lbicKIyAgICkKIyAKIyBnZ21hcChiYXNlX21hcCkgKwojICAgIyBnZW9tX3BvaW50KAojICAgIyAgIGRhdGEgPSByZXBlYXRfb2ZmZW5kZXJfbG9jYXRpb25zICU+JQojICAgIyAgICAgZmlsdGVyKCFpcy5uYShsb24pLAojICAgIyAgICAgICAgICAgIG4gPj0gMyksCiMgICAjICAgYWVzKHggPSBsb24sIHkgPSBsYXQpLAojICAgIyAgIGNvbG9yID0gImJsdWUiLAojICAgIyAgIHNpemUgPSAyCiMgICAjICkgIAojICAgZ2VvbV9wb2x5Z29uKAojICAgICBkYXRhID0gZm9ydGlmeShob29kX21ldHJpY3Nfb3ZlcjE1MCkgJT4lCiMgICAgICAgZmlsdGVyKCFpcy5uYShjcmltZV9ncmltZV9pbmRleCkpLAojICAgICBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBjcmltZV9ncmltZV9pbmRleCksIAojICAgICBhbHBoYSA9IDAuOSxjb2xvciA9ICJibGFjayIKIyAgICkjICsKIyAgICMgc2NhbGVfZmlsbF92aXJpZGlzX2MobmEudmFsdWUgPSBOQSwgCiMgICAjICAgICAgICAgICAgICAgICAgICAgICN2YWx1ZXMgPSByZXNjYWxlKGMoMCwgMjAsIDcwKSwgYygwLDEpKSwKIyAgICMgICAgICAgICAgICAgICAgICAgICAgb3B0aW9uID0gInBsYXNtYSIsCiMgICAjICAgICAgICAgICAgICAgICAgICAgIGd1aWRlX2NvbG9yYmFyKHRpdGxlID0gIkFubnVhbCBEViBJbmNpZGVudHNcbnBlciAxLDAwMCBSZXNpZGVudHMiKSkKIyAgICMgCmBgYAoKYGBge3J9Cmhvb2RzQGRhdGEgPC0gaG9vZHNAZGF0YSAlPiUKICBsZWZ0X2pvaW4oCiAgICBob29kX21ldHJpY3Nfb3ZlcjE1MEBkYXRhICU+JSBzZWxlY3QoLUFDU18yMDEzXzIwMTcpLAogICAgYnkgPSAibGFiZWwiCiAgKQpgYGAKCgpgYGB7cn0KIyBjb2xvciBwYWxldHRlIGZvciBtYXAKcGFsIDwtICBjb2xvck51bWVyaWMoInZpcmlkaXMiLCAKICAgICAgICAgICAgICAgICAgICAgZG9tYWluID0gaG9vZHMkY3JpbWVfZ3JpbWVfaW5kZXgsCiAgICAgICAgICAgICAgICAgICAgIG5hLmNvbG9yID0gTkEpCgojIGxhYmVscwpob29kc0BkYXRhIDwtIGhvb2RzQGRhdGEgJT4lCiAgbXV0YXRlKG1hcF9sYWJlbCA9IHBhc3RlMCgKICAgICI8Yj4iLGhvb2RzJGxhYmVsLCAiPC9iPjxicj4iLAogICAgIlBvcHVsYXRpb246ICIsIHJvdW5kKGhvb2RzJEFDU18yMDEzXzIwMTcsIDApLCAiPGJyPiIsCiAgICAiQ3JpbWUgJiBHcmltZSBJbmRleDogIiwgcm91bmQoaG9vZHMkY3JpbWVfZ3JpbWVfaW5kZXgsIDIpLCAiPGJyPiIsCiAgICAiQ3JpbWUgSW5kZXg6ICIsIHJvdW5kKGhvb2RzJGNyaW1lX2luZGV4LCAyKSwgIjxicj4iLAogICAgIkdyaW1lIEluZGV4OiAiLCByb3VuZChob29kcyRncmltZV9pbmRleCwgMiksICI8YnI+IiwKICAgICI8YnI+PGI+MjAxOSBDcmltZTwvYj48YnI+IiwKICAgICJIb21pY2lkZXMgKyBTaG9vdGluZ3M6ICIsIGhvb2RzJGhvbWljaWRlX3Nob290aW5ncywgIjxicj4iLAogICAgIlJhcGU6ICIsIGhvb2RzJHJhcGUsICI8YnI+IiwKICAgICJBZ2dyYXZhdGVkIEFzc2F1bHQ6ICIsIGhvb2RzJGFnZ19hc3NhdWx0LCAiPGJyPiIsCiAgICAiUm9iYmVyeTogIiwgaG9vZHMkcm9iYmVyeSwgIjxicj4iLAogICAgIjxicj48Yj4yMDE5IFNlcnZpY2UgUmVxdWVzdHM8L2I+PGJyPiIsCiAgICAiSWxsZWdhbCBEdW1waW5nIFNSczogIiwgaG9vZHMkaWxsZWdhbF9kdW1waW5nLCAiPGJyPiIsCiAgICAiUHJvcGVydHkgTWFpbnRlbmFuY2UgU1JzOiAiLCBob29kcyRwcm9wZXJ0eV9tYWludGVuYW5jZSwgIjxicj4iLAogICAgIlJpZ2h0LW9mLVdheSBTUnM6ICIsIGhvb2RzJHJpZ2h0X29mX3dheSwgIjxicj4iCikpCmBgYAoKYGBge3IgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDZ9CmxlYWZsZXQoKSAlPiUKICBzZXRWaWV3KGxuZyA9IC03Ni42LCBsYXQgPSAzOS4zLCB6b29tID0gMTEpICU+JQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJFN0YW1lbi5Ub25lckxpdGUpICU+JSAKICBhZGRQb2x5Z29ucyhkYXRhID0gaG9vZHMsCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMiwKICAgICAgICAgICAgICBmaWxsQ29sb3IgID0gfnBhbChob29kcyRjcmltZV9ncmltZV9pbmRleCksCiAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjksCiAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjc1LAogICAgICAgICAgICAgIGxhYmVsID0gfmxhcHBseShob29kcyRtYXBfbGFiZWwsIEhUTUwpKSAlPiUKICBhZGRDb250cm9sKCJCYWx0aW1vcmU8YnI+Q3JpbWUgJiBHcmltZSBJbmRleCIsIHBvc2l0aW9uID0gInRvcGxlZnQiKQpgYGAKCiMjIERpc3RyaWJ1dGlvbiBvZiBJbmRleGVzCgpCZWNhdXNlIG9mIHRoZSBob21pY2lkZSBhbmQgc2hvb3RpbmcgaW5kZXggYmVpbmcgMSBmb3IgbmVpZ2hib3Job29kcyB3aXRoIGF0IGxlYXN0IG9uZSBob21pY2lkZSBvciBzaG9vdGluZyBhbmQgMCBmb3IgYWxsIG90aGVycywgdGhlIGRpc3RyaWJ1dGlvbiBpcyBiaWZ1cmNhdGVkIGludG8gbmVpZ2hib3Job29kcyB3aXRoIGFuZCB3aXRob3V0IHNob290aW5ncy4KCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMSwgZmlnLndpZHRoID0gMn0KaG9vZHNAZGF0YSAlPiUKICBnZ3Bsb3QoYWVzKGNyaW1lX2dyaW1lX2luZGV4KSkgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIHRoZW1lX2l0ZWFtX2dvb2dsZV9kb2NzKCkgKwogIGdlb21fdGV4dChhZXMoMC4zLCAyNSwgCiAgICAgICAgICAgICAgICBsYWJlbD0iQXQgTGVhc3QgMSBIb21pY2lkZSBvciBTaG9vdGluZ1xuaW4gMjAxOSIpLCBzaXplID0gMiwgaGp1c3QgPSAwKSArCiAgZ2VvbV90ZXh0KGFlcygwLjA0LCAyNSwgCiAgICAgICAgICAgICAgICBsYWJlbD0iTm8gSG9taWNpZGVzIG9yIFNob290aW5nc1xuaW4gMjAxOSIpLCBzaXplID0gMiwgaGp1c3QgPSAwKQpgYGAKCiMjIFRoZSBMaXN0LCBJbiBPcmRlciBvZiBEZXNjZW5kaW5nIENyaW1lLUdyaW1lIEluZGV4CgpTb21lIG5laWdoYm9yaG9vZHMgd2l0aCB2ZXJ5IHNtYWxsIHBvcHVsYXRpb25zIHN0aWxsIHNuZWFrIG9udG8gdGhlIGxpc3QgYmVjYXVzZSB3ZSBvbmx5IHVzZWQgMTUwIGFzIHRoZSBjdXQtb2ZmLCBidXQgdGhvc2UgY2FuIGJlIG1hbnVhbGx5IGRyb3BwZWQgaWYgbm90IGludGVyZXN0ZWQuCgpgYGB7cn0KdGhlX2xpc3QgPC0gaG9vZHNAZGF0YSAlPiUKICBhcnJhbmdlKGRlc2MoY3JpbWVfZ3JpbWVfaW5kZXgpKSAlPiUKICBzZWxlY3QoLWFjcmVzLCAtbmJyZGVzYywgLXNoYXBlX2xlbmcsIC1zaGFwZV9hcmVhLCAtY29sb3JfMiwgLURlY2VubmlhbDIwMTApICU+JQogIHJlbmFtZShuZWlnaGJvcmhvb2QgPSBsYWJlbCwKICAgICAgICAgcG9wdWxhdGlvbl9hY3NfMjAxM18yMDE3ID0gQUNTXzIwMTNfMjAxNykKCndyaXRlX2Nzdih0aGVfbGlzdCwgIi4uL2RhdGEvcHJvY2Vzc2VkL2NyaW1lX2dyaW1lX2luZGV4X3YzLmNzdiIpCgp0aGVfbGlzdApgYGAKCmBgYHtyfQp0b3BfMjAgPC0gaG9vZHMgJT4lCiAgYXJyYW5nZShkZXNjKGNyaW1lX2dyaW1lX2luZGV4KSkgJT4lCiAgdG9wX24obiA9IDIwLCB3dCA9IGNyaW1lX2dyaW1lX2luZGV4KQpgYGAKCmBgYHtyIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2fQpsZWFmbGV0KCkgJT4lCiAgc2V0VmlldyhsbmcgPSAtNzYuNiwgbGF0ID0gMzkuMywgem9vbSA9IDExKSAlPiUKICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRTdGFtZW4uVG9uZXJMaXRlKSAlPiUKICBhZGRQb2x5Z29ucyhkYXRhID0gY2l0eWxpbmUsCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMiwKICAgICAgICAgICAgICAjZmlsbENvbG9yICA9IGl0ZWFtLmNvbG9yc1sxXSwKICAgICAgICAgICAgICBjb2xvciA9IGl0ZWFtLmNvbG9yc1syXSwKICAgICAgICAgICAgICBvcGFjaXR5ID0gMC45LAogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC4wKSAlPiUKICBhZGRQb2x5Z29ucyhkYXRhID0gdG9wXzIwLAogICAgICAgICAgICAgIHdlaWdodCA9IDIsCiAgICAgICAgICAgICAgZmlsbENvbG9yICA9IGl0ZWFtLmNvbG9yc1sxXSwKICAgICAgICAgICAgICBjb2xvciA9IGl0ZWFtLmNvbG9yc1syXSwKICAgICAgICAgICAgICBvcGFjaXR5ID0gMC45LAogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC43NSwKICAgICAgICAgICAgICBwb3B1cCA9IH5sYXBwbHkodG9wXzIwJG1hcF9sYWJlbCwgSFRNTCksCiAgICAgICAgICAgICAgbGFiZWwgPSB0b3BfMjAkbGFiZWwsCiAgICAgICAgICAgICAgbGFiZWxPcHRpb25zID0gbGFiZWxPcHRpb25zKG5vSGlkZSA9IFQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0T25seSA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ICdjZW50ZXInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0c2l6ZSA9IDIpKSAlPiUKCiAgYWRkQ29udHJvbCgiQmFsdGltb3JlPGJyPkNyaW1lICYgR3JpbWUgSW5kZXg8YnI+VG9wIDIwIE5laWdoYm9yaG9vZHM8YnI+PGJyPkNsaWNrIE5laWdoYm9yaG9vZDxicj5Gb3IgTW9yZSBJbmZvIiwgCiAgICAgICAgICAgICBwb3NpdGlvbiA9ICJ0b3BsZWZ0IikKYGBgCgoK